"""
资源监控工具 - 任务栏显示功能
实现在Windows任务栏上直接显示CPU和内存使用率信息
"""

import tkinter as tk
import threading
import time
import monitor
import traceback
import ctypes
from ctypes import wintypes
import sys
import os

# 添加Windows API常量
SWP_NOSIZE = 0x0001
SWP_NOMOVE = 0x0002
SWP_NOACTIVATE = 0x0010
HWND_TOPMOST = -1
WS_EX_TOOLWINDOW = 0x00000080
WS_EX_APPWINDOW = 0x00040000
GWL_EXSTYLE = -20

class TaskbarDisplay:
    """在Windows任务栏上显示系统资源使用情况"""
    
    def __init__(self, window_config=None, position_callback=None):
        """初始化任务栏显示
        
        Args:
            window_config (dict): 窗口配置信息，包含x, y, width, height
            position_callback (callable): 位置改变时的回调函数
        """
        self.running = False
        self.visible = False
        self.window = None
        self.update_thread = None
        self.refresh_interval = 2  # 刷新间隔（秒）
        self.update_needed = False  # 标记是否需要更新标题
        self.topmost_check_interval = 1  # 检查置顶状态的间隔（秒）
        
        # 位置管理相关
        self.window_config = window_config or {}
        self.position_callback = position_callback
        self.last_save_time = 0  # 用于防抖动
        self.save_delay = 1.0  # 位置保存延迟（秒）
        self.drag_active = False  # 标记是否正在拖动
        
        # 初始化数据
        try:
            self.cpu_usage = monitor.get_cpu_usage()
            self.memory_usage = monitor.get_memory_usage()
        except:
            self.cpu_usage = 0
            self.memory_usage = 0
        
        # 创建窗口
        self._create_window()
        
        # 启动定时更新器
        if self.window:
            # 初始化UI
            self.update_needed = True
            self._update_title_if_needed()
            self._setup_periodic_update()
            self._ensure_always_visible()
        
        # 默认先隐藏窗口
        self.hide()
    
    def _validate_position(self, x, y, width, height):
        """验证窗口位置是否在屏幕范围内
        
        Args:
            x, y, width, height: 窗口位置和大小
            
        Returns:
            tuple: (有效的x, 有效的y) 或 None（如果位置完全无效）
        """
        try:
            # 获取屏幕尺寸
            temp_root = tk.Tk()
            temp_root.withdraw()
            screen_width = temp_root.winfo_screenwidth()
            screen_height = temp_root.winfo_screenheight()
            temp_root.destroy()
            
            # 检查窗口是否至少有一部分在屏幕内
            if (x + width > 0 and x < screen_width and 
                y + height > 0 and y < screen_height):
                
                # 调整位置确保窗口不会完全超出屏幕
                if x < 0:
                    x = 0
                elif x + width > screen_width:
                    x = screen_width - width
                    
                if y < 0:
                    y = 0
                elif y + height > screen_height:
                    y = screen_height - height
                    
                return (x, y)
            else:
                return None
                
        except Exception as e:
            print(f"验证窗口位置时出错: {e}")
            return None
    
    def get_current_position(self):
        """获取当前窗口位置和大小
        
        Returns:
            dict: 包含x, y, width, height的字典，如果窗口不存在则返回None
        """
        try:
            if self.window and self.window.winfo_exists():
                return {
                    'x': self.window.winfo_x(),
                    'y': self.window.winfo_y(),
                    'width': self.window.winfo_width(),
                    'height': self.window.winfo_height()
                }
            return None
        except Exception as e:
            print(f"获取窗口位置时出错: {e}")
            return None
    
    def _save_position_delayed(self):
        """延迟保存位置（防抖动）"""
        def save_position():
            current_time = time.time()
            if current_time - self.last_save_time >= self.save_delay:
                # 保存当前位置
                if self.position_callback:
                    position = self.get_current_position()
                    if position:
                        try:
                            self.position_callback(position)
                            print(f"保存窗口位置: {position}")
                        except Exception as e:
                            print(f"保存位置回调时出错: {e}")
        
        self.last_save_time = time.time()
        if self.window:
            self.window.after(int(self.save_delay * 1000), save_position)
    
    def _create_window(self):
        """创建任务栏窗口"""
        try:
            # 创建一个极小的窗口，主要用于在任务栏显示信息
            self.window = tk.Tk()
            
            # 设置窗口样式为工具窗口并移除标题栏
            self.window.overrideredirect(True)  # 移除标题栏
            
            # 设置窗口大小（优先使用配置中的大小）
            window_width = self.window_config.get('width', 120)
            window_height = self.window_config.get('height', 25)
            
            # 确定窗口位置
            if ('x' in self.window_config and 'y' in self.window_config):
                # 使用保存的位置
                x = self.window_config['x']
                y = self.window_config['y']
                
                # 验证位置是否有效
                validated_pos = self._validate_position(x, y, window_width, window_height)
                if validated_pos:
                    x, y = validated_pos
                    print(f"使用保存的窗口位置: {x}, {y}")
                else:
                    print("保存的位置无效，使用默认位置计算")
                    # 如果保存的位置无效，使用默认计算
                    taskbar_info = self._get_taskbar_info()
                    x, y = self._calculate_default_position(taskbar_info, window_width, window_height)
            else:
                print("没有保存的位置，使用默认位置计算")
                # 如果没有保存的位置，使用默认计算
                taskbar_info = self._get_taskbar_info()
                x, y = self._calculate_default_position(taskbar_info, window_width, window_height)
            
            # 设置窗口位置和大小
            self.window.geometry(f"{window_width}x{window_height}+{x}+{y}")
            
            # 禁止调整大小
            self.window.resizable(False, False)
            
            # 始终置顶，确保在任务栏区域可见
            self.window.wm_attributes("-topmost", True)
            
            # 设置背景色
            self.window.configure(bg='#202020')  # 深色背景，类似任务栏
            
            # 设置点击和拖动事件
            self.window.bind("<Button-1>", self._on_click)
            self.window.bind("<B1-Motion>", self._on_drag)
            self.window.bind("<ButtonRelease-1>", self._on_drag_end)
            
            # 使用Windows API设置窗口样式
            self._set_window_style()
            
            print("任务栏窗口已创建")
        except Exception as e:
            print(f"创建任务栏窗口时出错: {e}")
            traceback.print_exc()
    
    def _calculate_default_position(self, taskbar_info, window_width, window_height):
        """计算默认窗口位置"""
        if taskbar_info["position"] == "bottom":
            x = taskbar_info["screen_width"] - window_width - 5
            y = taskbar_info["screen_height"] - taskbar_info["size"] + 2
        elif taskbar_info["position"] == "top":
            x = taskbar_info["screen_width"] - window_width - 5
            y = 2
        elif taskbar_info["position"] == "left":
            x = taskbar_info["size"] - window_width + 2
            y = taskbar_info["screen_height"] - window_height - 5
        else:  # right
            x = taskbar_info["screen_width"] - taskbar_info["size"] - window_width + 2
            y = taskbar_info["screen_height"] - window_height - 5
        
        return x, y
    
    def _get_taskbar_info(self):
        """获取任务栏信息"""
        info = {
            "screen_width": 0,
            "screen_height": 0,
            "position": "bottom",  # 默认底部
            "size": 40  # 默认高度
        }
        
        try:
            # 获取屏幕尺寸
            temp_root = tk.Tk()
            temp_root.withdraw()
            info["screen_width"] = temp_root.winfo_screenwidth()
            info["screen_height"] = temp_root.winfo_screenheight()
            temp_root.destroy()
            
            # 尝试检测任务栏位置（简化版）
            # 这只是一个粗略估计，完整检测需要使用Windows API
            # 如果你的电脑任务栏不在底部，这个估计可能不准确
            user32 = ctypes.windll.user32
            monitor_info = wintypes.RECT()
            
            monitor = user32.MonitorFromWindow(0, 0)  # 获取主显示器
            if monitor:
                # 获取工作区域（不包括任务栏）
                if user32.GetMonitorInfoW(monitor, ctypes.byref(monitor_info)):
                    # 比较工作区域和屏幕尺寸来推断任务栏位置
                    if monitor_info.bottom < info["screen_height"]:
                        info["position"] = "bottom"
                        info["size"] = info["screen_height"] - monitor_info.bottom
                    elif monitor_info.top > 0:
                        info["position"] = "top"
                        info["size"] = monitor_info.top
                    elif monitor_info.left > 0:
                        info["position"] = "left"
                        info["size"] = monitor_info.left
                    elif monitor_info.right < info["screen_width"]:
                        info["position"] = "right"
                        info["size"] = info["screen_width"] - monitor_info.right
        except Exception as e:
            print(f"获取任务栏信息时出错: {e}")
            traceback.print_exc()
        
        return info
    
    def _set_window_style(self):
        """设置窗口样式，尝试使其更像任务栏组件"""
        try:
            user32 = ctypes.windll.user32
            hwnd = self.window.winfo_id()
            
            # 获取当前窗口样式
            style = user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
            
            # 添加工具窗口样式，移除应用窗口样式
            new_style = (style | WS_EX_TOOLWINDOW) & ~WS_EX_APPWINDOW
            user32.SetWindowLongW(hwnd, GWL_EXSTYLE, new_style)
            
            # 设置窗口为始终置顶
            user32.SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE)
        except Exception as e:
            print(f"设置窗口样式时出错: {e}")
            traceback.print_exc()
    
    def _on_click(self, event):
        """处理窗口点击事件"""
        self.drag_start_x = event.x
        self.drag_start_y = event.y
        self.drag_active = True
    
    def _on_drag(self, event):
        """处理窗口拖动事件，允许用户调整窗口位置"""
        if self.drag_active:
            x = self.window.winfo_x() + (event.x - self.drag_start_x)
            y = self.window.winfo_y() + (event.y - self.drag_start_y)
            self.window.geometry(f"+{x}+{y}")
    
    def _on_drag_end(self, event):
        """处理窗口拖动结束事件"""
        self.drag_active = False
        self._save_position_delayed()
    
    def _setup_periodic_update(self):
        """设置周期性更新标题的定时器"""
        if self.window and self.visible:
            self._update_title_if_needed()
        # 每500毫秒检查一次是否需要更新标题
        if self.window and self.running:
            self.window.after(500, self._setup_periodic_update)
    
    def _ensure_always_visible(self):
        """确保窗口始终可见，即使切换应用程序"""
        if self.window and self.visible:
            # 刷新窗口置顶状态
            self.window.lift()
            self.window.attributes('-topmost', True)
            self._set_window_style()
        
        # 周期性检查
        if self.window and self.running:
            self.window.after(int(self.topmost_check_interval * 1000), self._ensure_always_visible)
    
    def _update_title_if_needed(self):
        """如果需要则更新显示内容"""
        if self.update_needed and self.visible:
            try:
                # 清空窗口中的所有控件
                for widget in self.window.winfo_children():
                    widget.destroy()
                
                # 创建单个标签显示所有信息，使用更紧凑的格式
                info_text = f"CPU:{self.cpu_usage:.0f}% 内存:{self.memory_usage:.0f}%"
                info_label = tk.Label(
                    self.window, 
                    text=info_text, 
                    bg='#202020',  # 深色背景
                    fg='#FFFFFF',   # 白色文本
                    font=("Arial", 9, "bold"),
                    padx=2,
                    pady=0
                )
                info_label.pack(fill=tk.BOTH, expand=True)
                
                self.update_needed = False
            except Exception as e:
                print(f"更新任务栏显示时出错: {e}")
                traceback.print_exc()
    
    def _show_impl(self):
        """在主线程中实际执行显示窗口操作"""
        try:
            if self.window:
                # 先确保数据更新
                self.cpu_usage = monitor.get_cpu_usage()
                self.memory_usage = monitor.get_memory_usage()
                self.update_needed = True
                self._update_title_if_needed()
                
                # 然后显示窗口
                self.window.deiconify()
                self.visible = True
                
                # 确保窗口置顶显示
                self.window.lift()
                self.window.attributes('-topmost', True)
                self._set_window_style()
                
                print("任务栏窗口已显示")
                
                # 启动更新循环
                if not self.running:
                    self.running = True
                    self._setup_periodic_update()
                    self._ensure_always_visible()
                    # 启动数据收集线程
                    self.update_thread = threading.Thread(target=self._update_thread, daemon=True)
                    self.update_thread.start()
                    print("任务栏更新线程已启动")
        except Exception as e:
            print(f"显示任务栏窗口实现时出错: {e}")
            traceback.print_exc()
    
    def show(self):
        """显示任务栏窗口并启动更新线程"""
        if not self.window:
            self._create_window()
        
        try:
            # 在主线程中执行窗口操作
            if self.window:
                self.window.after(0, self._show_impl)
        except Exception as e:
            print(f"显示任务栏窗口时出错: {e}")
            traceback.print_exc()
    
    def _hide_impl(self):
        """在主线程中实际执行隐藏窗口操作"""
        try:
            if self.window:
                self.window.withdraw()
                self.visible = False
                print("任务栏窗口已隐藏")
        except Exception as e:
            print(f"隐藏任务栏窗口实现时出错: {e}")
            traceback.print_exc()
    
    def hide(self):
        """隐藏任务栏窗口"""
        try:
            # 在主线程中执行窗口操作
            if self.window:
                self.window.after(0, self._hide_impl)
        except Exception as e:
            print(f"隐藏任务栏窗口时出错: {e}")
            traceback.print_exc()
    
    def update_title(self):
        """更新窗口显示CPU和内存使用率"""
        try:
            if self.window and self.visible:
                # 获取CPU和内存使用率
                self.cpu_usage = monitor.get_cpu_usage()
                self.memory_usage = monitor.get_memory_usage()
                self.update_needed = True
        except Exception as e:
            print(f"更新任务栏显示时出错: {e}")
            traceback.print_exc()
    
    def _update_thread(self):
        """后台更新线程 - 只收集数据，不直接操作UI"""
        try:
            while self.running:
                if self.visible:
                    # 收集数据，但不直接更新UI
                    self.update_title()
                
                # 休眠指定时间
                time.sleep(self.refresh_interval)
        except Exception as e:
            print(f"任务栏更新线程出错: {e}")
            traceback.print_exc()
    
    def _shutdown_impl(self):
        """在主线程中实际执行关闭窗口操作"""
        try:
            if self.window:
                self.window.destroy()
                self.window = None
                print("任务栏窗口已销毁")
        except Exception as e:
            print(f"关闭任务栏窗口实现时出错: {e}")
            traceback.print_exc()
    
    def shutdown(self):
        """关闭任务栏显示"""
        try:
            self.running = False
            if self.update_thread and self.update_thread.is_alive():
                self.update_thread.join(1)  # 等待线程结束（最多1秒）
            
            # 在主线程中执行窗口操作
            if self.window:
                try:
                    self.window.after(0, self._shutdown_impl)
                    # 给一点时间让after事件执行
                    time.sleep(0.1)
                except Exception as e:
                    print(f"调度窗口销毁操作时出错: {e}")
                    traceback.print_exc()
            
            print("任务栏显示已关闭")
            sys.exit(0)  # 确保程序完全退出
        except Exception as e:
            print(f"关闭任务栏显示时出错: {e}")
            traceback.print_exc()

# 直接运行此文件时
if __name__ == "__main__":
    try:
        print("启动独立任务栏显示...")
        taskbar = TaskbarDisplay()
        taskbar.show()  # 立即显示窗口
        
        # 使用主窗口的事件循环
        try:
            taskbar.window.mainloop()
        except KeyboardInterrupt:
            taskbar.shutdown()
            
    except Exception as e:
        print(f"任务栏显示运行出错: {e}")
        traceback.print_exc() 